﻿using System;
using System.Collections;
using System.Text;
using System.IO;
using AZEG;

namespace CipherBlok
{
    class Operation
    {
        // Data
        public BitArray[] BlokPlain;
        public BitArray[] BlokCipher;
        public BitArray Kunci;
        public BitArray IV;
        public int Progress;    // Progress Enkripsi/Dekripsi (dalam %)
        public int cur, tot;
        public Feistel f = new Feistel();
        
        // Constructor
        public void CipherBlok()
        {
            BlokPlain = new BitArray[65536];
            BlokCipher = new BitArray[65536];
            Kunci = new BitArray(64);
            IV = new BitArray(64);
        }

        // Method
        public void PrintBytes(byte[] bytes)
        {
            Console.Write("Length=");
            Console.WriteLine(bytes.GetLength(0));
            for (int i = 0; i < bytes.GetLength(0); i++)
            {
                Console.Write(bytes[i]); Console.Write(" ");
            }
            Console.WriteLine();
        }

        public void PrintBits(BitArray bits)
        {
            Console.Write(bits.Length);
            Console.Write(" bits | ");
            for (int i = 0; i < bits.Length; i++)
            {
                if (bits[i])
                    Console.Write(1);
                else
                    Console.Write(0);
            }
            Console.WriteLine();
        }
        
        public void KunciRetriever(String Key)
        { // Memperoleh 64bit Kunci yg diperoleh dari Key (String 8 karakter);
            byte[] temp;                // Temporary 1 byte
            byte[] kTemp = new byte[8]; // Temporary 8 byte
            for (int i = 0; i < Key.Length; i++)
            {
                // char to byte
                temp = BitConverter.GetBytes(Key[i]);
                kTemp[i] = temp[0];
            }
            Kunci = new BitArray(kTemp);
        }

        public void IVGenerator()
        {
            IV = NOT(Kunci);
        }

        public byte[] ReadFile(String filePath)
        {
            byte[] buffer = null;
            FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            try
            {
                int length = (int)fileStream.Length;  // get file length
                buffer = new byte[length];            // create buffer
                int count;                            // actual number of bytes read
                int sum = 0;                          // total number of bytes read

                // read until Read method returns 0 (end of the stream has been reached)
                while ((count = fileStream.Read(buffer, sum, length - sum)) > 0)
                    sum += count;  // sum is a buffer offset for next reading
            }
            finally
            {
                fileStream.Close();
            }
            return buffer;
        }

        public void WriteFile(String filePath, byte[] buffer)
        {
            FileStream fileStream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write);
            try
            {
                fileStream.SetLength(buffer.Length);
                fileStream.Write(buffer, 0, buffer.Length);
            }
            finally
            {
                fileStream.Close();
            }
        }

        public BitArray[] FileRetriever(String filePath)
        { // Menghasilkan array of BitArray dari pembacaan file
            BitArray[] Blok;
            byte[] kTemp = new byte[8]; // Temporary 8 byte
            byte[] buff = ReadFile(filePath);
            int nBlok = (buff.Length / 8); if (buff.Length % 8 > 0) nBlok++; // Banyaknya BlokPlain
            Blok = new BitArray[nBlok];
            int i = 0;
            for (int n = 0; n < nBlok; n++)
            {
                while ((i < ((n + 1) * 8)) && (i < buff.Length))
                {
                    kTemp[i % 8] = buff[i];
                    i++;
                }
                // Memasukkan Padding Bit
                if ((i % 8) > 0)
                {
                    i = i % 8;
                    for (int j = 7; j >= 0; j--)
                    {
                        i--;
                        if (i >= 0)
                            kTemp[j] = kTemp[i];
                        else
                            kTemp[j] = 0;
                    }
                }
                Blok[n] = new BitArray(kTemp);
            }
            return Blok;
        }

        public void FileProducer(String filePath, BitArray[] Blok)
        { // Menghasilkan File dari Blok (array of BitArray)
            byte[] buffer = new byte[Blok.Length * 8];
            byte[] kTemp = new byte[8];
            for (int n = 0; n < Blok.Length; n++)
            {
                Blok[n].CopyTo(kTemp, 0);
                for (int i = 0; i < 8; i++)
                {
                    buffer[(n*8)+i] = kTemp[i];
                }
            }
            WriteFile(filePath, buffer);
        }

        public BitArray Encrypt(BitArray Plain, BitArray Key)
        { // 64bit Plain, Key, & Cipher (Sementara dummy dulu, harusnya dari Fikri & Eme)
            return new BitArray(XOR(Plain,Key));
        }

        public BitArray Decrypt(BitArray Cipher, BitArray Key)
        { // 64bit Plain, Key, & Cipher (Sementara dummy dulu, harusnya dari Fikri & Eme)
            return new BitArray(XOR(Cipher,Key));
        }

        public BitArray XOR(BitArray A, BitArray B)
        { // Operasi XOR bikinan sendiri
            if (A.Length != B.Length)
                return new BitArray(0);
            else
            {
                BitArray C = new BitArray(A.Length);
                for (int i = 0; i < A.Length; i++)
                {
                    C[i] = (A[i] != B[i]);
                }
                return C;
            }
        }

        public BitArray NOT(BitArray A)
        { // Operasi NOT bikinan sendiri
            BitArray B = new BitArray(A.Length);
            for (int i = 0; i < A.Length; i++)
            {
                B[i] = !(A[i]);
            }
            return B;
        }

        public void InitProgress()
        {
            Progress = 0; //DisplayProgress();
        }

        public void UpdateProgress()
        {
            Progress = (100 * cur) / tot; //DisplayProgress();
        }

        public void FinishProgress()
        {
            Progress = 100; //DisplayProgress();
        }

        public void DisplayProgress()
        {
            Console.Clear();
            Console.Write(Progress + "%" + " Completed.");
        }

        public void ECB_Encrypt()
        { // Enkripsi ECB
            BlokCipher = new BitArray[BlokPlain.Length];
            InitProgress();
            tot = BlokPlain.Length;
            for (cur = 0; cur < tot; cur++)
            {
                //BlokCipher[cur] = Encrypt(BlokPlain[cur], Kunci);
                BlokCipher[cur] = f.Encryptor(BlokPlain[cur], Kunci);
                UpdateProgress();
            }
            FinishProgress();
        }

        public void ECB_Decrypt()
        { // Dekripsi ECB
            int Length = BlokCipher.Length;
            BitArray[] BlokPlain = new BitArray[Length];
            InitProgress();
            tot = BlokCipher.Length;
            for (cur = 0; cur < tot; cur++)
            {
                BlokPlain[cur] = f.Decryptor(BlokCipher[cur], Kunci);
                UpdateProgress();
            }
            FinishProgress();
        }

        public void CBC_Encrypt()
        { // Enkripsi CBC
            IVGenerator();
            BlokCipher = new BitArray[BlokPlain.Length];
            //BlokCipher[0] = Encrypt(XOR(BlokPlain[0],IV), Kunci);
            BlokCipher[0] = f.Encryptor(XOR(BlokPlain[0], IV), Kunci);
            InitProgress();
            tot = BlokPlain.Length;
            for (cur = 1; cur < tot; cur++)
            {
                //BlokCipher[cur] = Encrypt(XOR(BlokPlain[cur], BlokCipher[cur - 1]), Kunci);
                BlokCipher[cur] = f.Encryptor(XOR(BlokPlain[cur], BlokCipher[cur - 1]), Kunci);
                UpdateProgress();
            }
            FinishProgress();
        }

        public void CBC_Decrypt()
        { // Dekripsi CBC
            IVGenerator();
            BlokPlain = new BitArray[BlokCipher.Length];
            //BlokPlain[0] = XOR(Decrypt(BlokCipher[0], Kunci), IV);
            BlokPlain[0] = XOR(f.Decryptor(BlokCipher[0], Kunci), IV);
            InitProgress();
            tot = BlokCipher.Length;
            for (cur = 1; cur < tot; cur++)
            {
                //BlokPlain[cur] = XOR(Decrypt(BlokCipher[cur], Kunci), BlokCipher[cur - 1]);
                BlokPlain[cur] = XOR(f.Decryptor(BlokCipher[cur], Kunci), BlokCipher[cur - 1]);
                UpdateProgress();
            }
            FinishProgress();
        }

        public void CFB_Encrypt()
        { // Enkripsi CFB
            IVGenerator();
            BlokCipher = new BitArray[BlokPlain.Length];
            BitArray temp1 = IV;
            BlokCipher[0] = XOR(BlokPlain[0], temp1);
            InitProgress();
            tot = BlokPlain.Length;
            for (cur = 1; cur < tot; cur++)
            {
                //BlokCipher[cur] = XOR(Encrypt(BlokCipher[cur - 1], Kunci), BlokPlain[cur]);
                temp1 = f.Encryptor(BlokCipher[cur - 1], Kunci);
                BlokCipher[cur] = XOR(BlokPlain[cur], temp1);
                UpdateProgress();
            }
            FinishProgress();
        }

        public void CFB_Decrypt()
        { // Dekripsi CFB
            IVGenerator();
            BlokPlain = new BitArray[BlokCipher.Length];
            BitArray temp1 = IV;
            BlokPlain[0] = XOR(BlokCipher[0], temp1);
            InitProgress();
            tot = BlokCipher.Length;
            for (cur = 1; cur < tot; cur++)
            {
                //BlokPlain[cur] = XOR(Decrypt(BlokCipher[cur - 1], Kunci), BlokCipher[cur]);
                temp1 = f.Encryptor(BlokCipher[cur - 1], Kunci);
                BlokPlain[cur] = XOR(BlokCipher[cur], temp1);
                UpdateProgress();
            }
            FinishProgress();
        }

        public void OFB_Encrypt()
        { // Enkripsi OFB
            BitArray temp;
            IVGenerator();
            BlokCipher = new BitArray[BlokPlain.Length];
            temp = IV;
            BlokCipher[0] = XOR(temp, BlokPlain[0]);
            InitProgress();
            tot = BlokPlain.Length;
            for (cur = 1; cur < tot; cur++)
            {
                temp = f.Encryptor(temp, Kunci);
                //BlokCipher[cur] = XOR(Encrypt(temp, Kunci), BlokPlain[cur]);
                BlokCipher[cur] = XOR(f.Encryptor(temp, Kunci), BlokPlain[cur]);
                UpdateProgress();
            }
            FinishProgress();
        }
        
        public void OFB_Decrypt()
        { // Dekripsi OFB
            BitArray temp;
            IVGenerator();
            BlokPlain = new BitArray[BlokCipher.Length];
            temp = IV;
            BlokPlain[0] = XOR(temp, BlokCipher[0]);
            InitProgress();
            tot = BlokCipher.Length;
            for (cur = 1; cur < tot; cur++)
            {
                temp = f.Encryptor(temp, Kunci);
                //BlokPlain[cur] = XOR(Decrypt(temp, Kunci), BlokCipher[cur]);
                BlokPlain[cur] = XOR(f.Encryptor(temp, Kunci), BlokCipher[cur]);
                UpdateProgress();
            }
            FinishProgress();
        }
    }
}
